package gw.lang.annotation;
import gw.lang.reflect.IAnnotationInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.java.IJavaType;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* The class modifier describes how this annotation can be used on classes.
*
* Copyright 2010 Guidewire Software, Inc.
*/
public enum UsageModifier {
/**
* Use None to specify this annotation cannot exist on a class
*/
None,
/**
* Use None to specify this annotation can only appear once on a class
*/
One,
/**
* Use None to specify this annotation can appear many times on a class
*/
Many;
public static UsageModifier getUsageModifier( UsageTarget targetType, IType annotationType )
{
UsageModifier modifier = null;
//First look for gosu-style usage annotations
List<AnnotationUsage> usageInfos = getExplicitUsageAnnotations( annotationType );
if( usageInfos != null && usageInfos.size() > 0 )
{
return getUsageModifier( targetType, modifier, usageInfos );
}
// if it's a java annotation with no explicit annotation usage, translate the java element type information
else if( annotationType instanceof IJavaType &&
TypeSystem.get( Annotation.class ).isAssignableFrom( annotationType ) )
{
return translateJavaElementTypeToUsageModifier( targetType, annotationType );
}
else
{
// By default, gosu annotations can appear multiple times
return UsageModifier.Many;
}
}
private static List<AnnotationUsage> getExplicitUsageAnnotations( IType annotationType )
{
ArrayList<AnnotationUsage> lst = new ArrayList<AnnotationUsage>();
List<IAnnotationInfo> usageAnnotations = annotationType.getTypeInfo().getAnnotationsOfType( TypeSystem.get( AnnotationUsage.class ) );
if( usageAnnotations != null )
{
for( IAnnotationInfo iAnnotationInfo : usageAnnotations )
{
lst.add( (AnnotationUsage)iAnnotationInfo.getInstance() );
}
}
List<IAnnotationInfo> usagesAnnotations = annotationType.getTypeInfo().getAnnotationsOfType( TypeSystem.get( AnnotationUsages.class ) );
if( usagesAnnotations != null )
{
for( IAnnotationInfo iAnnotationInfo : usagesAnnotations )
{
lst.addAll( Arrays.asList( ((AnnotationUsages)iAnnotationInfo.getInstance()).value() ) );
}
}
return lst;
}
private static UsageModifier getUsageModifier( UsageTarget targetType, UsageModifier modifier, List<AnnotationUsage> usageInfos )
{
//If there are usages, then we must examine each one to find the one that most specifically applies
for( AnnotationUsage usage : usageInfos )
{
// If usage applies to all, and we haven't had a more specific match yet, get the modifier for it
if( usage.target().equals( UsageTarget.AllTarget ) && modifier == null )
{
modifier = usage.usageModifier();
}
// If usage applies to the given target, it always overrides whatever else we've seen
if( usage.target().equals( targetType ) )
{
modifier = usage.usageModifier();
}
}
// if no usage matched, then that implies that the usage is None.
if( modifier == null )
{
modifier = UsageModifier.None;
}
return modifier;
}
private static UsageModifier translateJavaElementTypeToUsageModifier( UsageTarget targetType, IType annotationType )
{
Target targetAnnotation = ((IJavaType)annotationType).getClassInfo().getAnnotation( Target.class );
if( targetAnnotation == null || targetAnnotation.value().length == 0 )
{
return UsageModifier.One; // If there are no targets, it can be used everywhere
}
else
{
// otherwise, look for a target that matches our own UsageTarget
for( ElementType elementType : targetAnnotation.value() )
{
UsageTarget usageTarget = null;
if( elementType == ElementType.CONSTRUCTOR && targetType == UsageTarget.ConstructorTarget ||
elementType == ElementType.FIELD && targetType == UsageTarget.PropertyTarget ||
elementType == ElementType.ANNOTATION_TYPE && targetType == UsageTarget.TypeTarget ||
elementType == ElementType.TYPE && targetType == UsageTarget.TypeTarget ||
elementType == ElementType.METHOD && targetType == UsageTarget.MethodTarget )
{
return UsageModifier.One;
}
}
return UsageModifier.None;
}
}
}